Typically, some devices in a system use bits of RAM for communication, and
these areas should be listed as reserved in the E820 table and identified
- via RMRR or IVMD entries in the APCI tables, so Xen can ensure that they
+ via RMRR or IVMD entries in the ACPI tables, so Xen can ensure that they
are identity-mapped in the IOMMU. However, some firmware makes mistakes,
and this option is a coarse-grain workaround for those errors.
Where possible, finer grain corrections should be made with the `rmrr=`,
- `ivrs_hpet=` or `ivrs_ioapic=` command line options.
+ `ivmd=`, `ivrs_hpet[]=`, or `ivrs_ioapic[]=` command line options.
This option is disabled by default, and deprecated and intended for
removal in future versions of Xen. If specifying `map-inclusive` is the
> `= <integer>`
### irq_vector_map (x86)
+
+### ivmd (x86)
+> `= <start>[-<end>][=<bdf1>[-<bdf1'>][,<bdf2>[-<bdf2'>][,...]]][;<start>...]`
+
+Define IVMD-like ranges that are missing from ACPI tables along with the
+device(s) they belong to, and use them for 1:1 mapping. End addresses can be
+omitted when exactly one page is meant. The ranges are inclusive when start
+and end are specified. Note that only PCI segment 0 is supported at this time,
+but it is fine to specify it explicitly.
+
+'start' and 'end' values are page numbers (not full physical addresses),
+in hexadecimal format (can optionally be preceded by "0x").
+
+Omitting the optional (range of) BDF spcifiers signals that the range is to
+be applied to all devices.
+
+Usage example: If device 0:0:1d.0 requires one page (0xd5d45) to be
+reserved, and devices 0:0:1a.0...0:0:1a.3 collectively require three pages
+(0xd5d46 thru 0xd5d48) to be reserved, one usage would be:
+
+ivmd=d5d45=0:1d.0;0xd5d46-0xd5d48=0:1a.0-0:1a.3
+
+Note: grub2 requires to escape or quote special characters, like ';' when
+multiple ranges are specified - refer to the grub2 documentation.
+
### ivrs_hpet[`<hpet>`] (AMD)
> `=[<seg>:]<bus>:<device>.<func>`
}
+static struct acpi_ivrs_memory __initdata user_ivmds[8];
+static unsigned int __initdata nr_ivmd;
+
#define to_ivhd_block(hdr) \
container_of(hdr, const struct acpi_ivrs_hardware, header)
#define to_ivmd_block(hdr) \
{
const struct acpi_ivrs_header *ivrs_block;
unsigned long length;
- unsigned int apic;
+ unsigned int apic, i;
bool_t sb_ioapic = !iommu_intremap;
int error = 0;
length += ivrs_block->length;
}
+ /* Add command line specified IVMD-equivalents. */
+ if ( nr_ivmd )
+ AMD_IOMMU_DEBUG("IVMD: %u command line provided entries\n", nr_ivmd);
+ for ( i = 0; !error && i < nr_ivmd; ++i )
+ error = parse_ivmd_block(user_ivmds + i);
+
/* Each IO-APIC must have been mentioned in the table. */
for ( apic = 0; !error && iommu_intremap && apic < nr_ioapics; ++apic )
{
{
return acpi_table_parse(ACPI_SIG_IVRS, get_supported_ivhd_type);
}
+
+/*
+ * Parse "ivmd" command line option to later add the parsed devices / regions
+ * into unity mapping lists, just like IVMDs parsed from ACPI.
+ * Format:
+ * ivmd=<start>[-<end>][=<bdf1>[-<bdf1>'][,<bdf2>[-<bdf2>'][,...]]][;<start>...]
+ */
+static int __init parse_ivmd_param(const char *s)
+{
+ do {
+ unsigned long start, end;
+ const char *cur;
+
+ if ( nr_ivmd >= ARRAY_SIZE(user_ivmds) )
+ return -E2BIG;
+
+ start = simple_strtoul(cur = s, &s, 16);
+ if ( cur == s )
+ return -EINVAL;
+
+ if ( *s == '-' )
+ {
+ end = simple_strtoul(cur = s + 1, &s, 16);
+ if ( cur == s || end < start )
+ return -EINVAL;
+ }
+ else
+ end = start;
+
+ if ( *s != '=' )
+ {
+ user_ivmds[nr_ivmd].start_address = start << PAGE_SHIFT;
+ user_ivmds[nr_ivmd].memory_length = (end - start + 1) << PAGE_SHIFT;
+ user_ivmds[nr_ivmd].header.flags = ACPI_IVMD_UNITY |
+ ACPI_IVMD_READ | ACPI_IVMD_WRITE;
+ user_ivmds[nr_ivmd].header.length = sizeof(*user_ivmds);
+ user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_ALL;
+ ++nr_ivmd;
+ continue;
+ }
+
+ do {
+ unsigned int seg, bus, dev, func;
+
+ if ( nr_ivmd >= ARRAY_SIZE(user_ivmds) )
+ return -E2BIG;
+
+ s = parse_pci(s + 1, &seg, &bus, &dev, &func);
+ if ( !s || seg )
+ return -EINVAL;
+
+ user_ivmds[nr_ivmd].start_address = start << PAGE_SHIFT;
+ user_ivmds[nr_ivmd].memory_length = (end - start + 1) << PAGE_SHIFT;
+ user_ivmds[nr_ivmd].header.flags = ACPI_IVMD_UNITY |
+ ACPI_IVMD_READ | ACPI_IVMD_WRITE;
+ user_ivmds[nr_ivmd].header.length = sizeof(*user_ivmds);
+ user_ivmds[nr_ivmd].header.device_id = PCI_BDF(bus, dev, func);
+ user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_ONE;
+
+ if ( *s == '-' )
+ {
+ s = parse_pci(s + 1, &seg, &bus, &dev, &func);
+ if ( !s || seg )
+ return -EINVAL;
+
+ user_ivmds[nr_ivmd].aux_data = PCI_BDF(bus, dev, func);
+ if ( user_ivmds[nr_ivmd].aux_data <
+ user_ivmds[nr_ivmd].header.device_id )
+ return -EINVAL;
+ user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_RANGE;
+ }
+ } while ( ++nr_ivmd, *s == ',' );
+ } while ( *s++ == ';' );
+
+ return s[-1] ? -EINVAL : 0;
+}
+custom_param("ivmd", parse_ivmd_param);